fix: unwrap() broken on wrapt 2.x due to ObjectProxy class identity mismatch#489
Open
thpierce wants to merge 2 commits intoaws:masterfrom
Open
fix: unwrap() broken on wrapt 2.x due to ObjectProxy class identity mismatch#489thpierce wants to merge 2 commits intoaws:masterfrom
thpierce wants to merge 2 commits intoaws:masterfrom
Conversation
3528f35 to
6769f3b
Compare
…ismatch In wrapt 2.x, wrapt.ObjectProxy points to wrapt.proxies.ObjectProxy (pure Python) while FunctionWrapper inherits from a different ObjectProxy in the C extension (_wrappers.so). This causes isinstance(f, wrapt.ObjectProxy) to return False, making unwrap() a silent no-op. Since unwrap() never actually unwraps, each patch/unpatch cycle in the test fixture accumulates another wrapper layer. By the 5th test (test_invalid_url), there are 5 nested wrappers creating 5 nested subsegments — only the innermost gets the real exception dict, outer ones get string cause IDs. Fix: remove the isinstance check from unwrap() — hasattr(f, '__wrapped__') is sufficient to identify wrapt-wrapped functions. This also fixes a user-facing bug: any application calling unpatch()/patch() (e.g. during reconfiguration) would accumulate wrapper layers, causing subsegment bloat on every patched call. Affects all ext modules using unwrap(), not just httplib.
6769f3b to
4115cbb
Compare
jj22ee
approved these changes
Mar 20, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
test_invalid_urlin the httplib ext test suite fails with aTypeErroron Python 3.8+ (where wrapt 2.x is installed). The test expectssubsegment.causeto be adict, but it gets astr(a cause ID reference to a nested subsegment).The root cause is that
unwrap()inaws_xray_sdk/ext/util.pyis a silent no-op on wrapt 2.x, which causes wrapper accumulation across patch/unpatch cycles.Root Cause
In wrapt 2.x, there are two different
ObjectProxyclasses:wrapt.ObjectProxy→wrapt.proxies.ObjectProxy(pure Python, new in 2.x)FunctionWrapperinherits from →_wrappers.ObjectProxy(C extension)These are different class objects with different
id()s. Theunwrap()function checks:This
isinstance()check always returnsFalsefor wrapt-wrapped functions on wrapt 2.x, sounwrap()never actually unwraps anything.Impact
Since
unwrap()is a no-op, everyunpatch()→patch()cycle adds another wrapper layer without removing the previous one:construct_ctxfixture callspatch()/unpatch()per test. By the 5th test (test_invalid_url),_send_requesthas 5 nested wrappers → 5 nested subsegments. Only the innermost gets the real exception dict; outer ones get string cause IDs →TypeError.unpatch()/patch()(e.g. during reconfiguration) accumulates wrapper layers, causing subsegment bloat on every patched call.unwrap(), not just httplib.Why exactly 5 subsegments (not infinite)
Not recursion — just test position.
test_invalid_urlis the 5th test function in the file. Each prior test adds one wrapper layer via the fixture's patch/unpatch cycle.Why it doesn't reproduce on macOS
The wrapt 2.x C extension wheel may not be available or may have a different class hierarchy on macOS, causing the pure-Python fallback to be used where the classes may be unified. On Linux (including GitHub Actions Ubuntu runners), the C extension
.sois always present.Fix
Remove the
isinstance(f, wrapt.ObjectProxy)check fromunwrap(). Thehasattr(f, "__wrapped__")check is sufficient to identify wrapt-wrapped functions and works across all wrapt versions:Verification
Confirmed locally on Linux with wrapt 2.1.2 + Python 3.12:
cause=dicttest_invalid_urlpasses with original test code (no workaround needed)Changes
aws_xray_sdk/ext/util.py: Remove brokenisinstance(f, wrapt.ObjectProxy)guard fromunwrap()tests/ext/httplib/test_httplib.py: No changes needed (original test code works with the fix)Ticket: apm-telegen-2729